/*
 * CGameWorld.cpp
 *
 *  Created on: 03.01.2013
 *      Author: Kirill Blinov
 *      Copyright: LGPL
 */

#include "CGameWorld.h"

CGameWorld::CGameWorld( CPhysicsParam *WorldPhysicsParam,
	    				CCameraParam *WorldCameraParam,
	    				std::vector<CGameObjectParam*> WorldGameObjectParams ) :
	    		worldPhysicsParam(WorldPhysicsParam),
	    		worldCameraParam(WorldCameraParam),
	    		worldGameObjectParams(WorldGameObjectParams),
	    		unitIteratorsAliveCounter(0)
{
	// worldPhysicsParam and worldCameraParam can't be NULL
	assert( worldPhysicsParam & worldCameraParam );
}

CGameWorld::~CGameWorld()
{
	// All iterators must be deleted
	assert( 0 == unitIteratorsAliveCounter );

	delete worldPhysicsParam;
	delete worldCameraParam;

	for ( std::vector<CGameObjectParam*>::iterator Begin = worldGameObjectParams.begin(),
		  End = worldGameObjectParams.end();
		  Begin != End;
		  Begin++ )
	{
		delete *Begin;
	}
}


bool
CGameWorld::CreateUnit( const CGameObjectParam &InitialParams,
		                const bool EnableNameIndexing,
		                const short OptimazeOption,
		                IUnit** CreatedUnit )
{
	IUnit *NewUnit = NULL;
	UNIT_INITIAL_PHYSIC_PARAMS UnitInitialParams( InitialParams.orientation,
												  InitialParams.position,
												  InitialParams.speed );

	bool Result = UnitFabric::CreateUnit( UnitInitialParams,
			                              InitialParams.unitTypeName,
			                              InitialParams.unitName,
			                              &NewUnit );

	//If result is TRUE NewUnit must exist
	assert( !Result || NewUnit );

	//If Unit with this name doesn't exist
	if ( Result )
	{
		if ( CREATE_UNIT_OPTIMAZE_ITERATION == OptimazeOption )
		{
			gameUnitsArray.push_back( NewUnit );
		}
		else if ( CREATE_UNIT_OPTIMAZE_DELETE == OptimazeOption )
		{
			gameUnitsList.push_back( NewUnit );
		}
		else
		{
			Result = false;
		}
	}

	if ( Result && EnableNameIndexing )
	{
		std::pair<boost::unordered_map< std::string, std::pair<IUnit*,UINT> >::iterator, bool> EmplaceResult;
		//Remember index of unit in array
		UINT UnitAddress = gameUnitsArray.size() | ( OptimazeOption << ( sizeof(UINT) - 1 ) );//Sorry for hardcoded shift value

		EmplaceResult = gameUnitsSet.emplace( InitialParams.unitName, std::pair<IUnit*,UINT>( NewUnit, UnitAddress ) );
		Result = EmplaceResult.second;
	}

	if ( !Result )
	{
		delete NewUnit;
		NewUnit = NULL;
	}

	if ( CreatedUnit != NULL )
	{
		*CreatedUnit = NewUnit;
	}

	return Result;
}


IUnit*
CGameWorld::GetUnit( const std::string Name ) const
{
	IUnit* Result = NULL;
	boost::unordered_map< std::string, std::pair<IUnit*,UINT> >::const_iterator Found;
	Found = gameUnitsSet.find( Name );

	if ( Found != gameUnitsSet.end() )
	{
		std::pair<IUnit*,UINT> ResulPair = Found->second;
		Result = ResulPair.first;
	}

	return Result;
}

CGameWorld::CGameWorldUnitIterator*
CGameWorld::GetWorldUnitsIterator() const
{
	CGameWorldUnitIterator *NewIterator =
			  new CGameWorldUnitIterator( &gameUnitsArray,
					                      &gameUnitsList,
					                      this );

	unitIteratorsAliveCounter++;
	return NewIterator;
}

void
CGameWorld::DestroyWorldUnitsIterator( CGameWorldUnitIterator* Iterator )const
{
	assert( this == Iterator );
	delete Iterator;
	unitIteratorsAliveCounter--;
}

void
CGameWorld::DeleteUnit( const std::string Name )
{
	boost::unordered_map< std::string, std::pair<IUnit*,UINT> >::iterator Found;
	Found = gameUnitsSet.find( Name );

	if ( Found != gameUnitsSet.end() )
	{
		std::pair<IUnit*,UINT> ResulPair = Found->second;
		UINT Address = ResulPair.second;
		short Optimization = ( Address >> ( sizeof(UINT) - 1 ) ) & CREATE_UNIT_MASK;
		UINT Index = Address & ~( CREATE_UNIT_MASK << ( sizeof(UINT) - 1 ) );

		if ( CREATE_UNIT_OPTIMAZE_ITERATION == Optimization )
		{
			//Erase pointer to avoid destructor call
			gameUnitsArray[Index] = NULL;
			std::vector<IUnit*>::iterator CurrentElement = gameUnitsArray.begin() + Index;
			gameUnitsArray.erase( CurrentElement );
		}
		else if ( CREATE_UNIT_OPTIMAZE_DELETE == Optimization )
		{
			gameUnitsList.assign( Index, NULL );

			std::list<IUnit*>::iterator CurrentElement = gameUnitsList.begin();
			for ( UINT i = 0; i < Index; i++ )
			{
				CurrentElement++;
			}
			gameUnitsList.erase( CurrentElement );
		}

		gameUnitsSet.erase( Found );
	}
}

void
CGameWorld::DeleteUnit( IUnit* GetUnit )
{
	assert( false );
}

void
CGameWorld::ResetGameWorld()
{
	CGameWorldUnitIterator* Iterator = GetWorldUnitsIterator();
	IUnit* CurrentUnit = Iterator->GetNextUnit();

	while ( CurrentUnit != NULL )
	{
		delete CurrentUnit;
		CurrentUnit = Iterator->GetNextUnit();
	}

	DestroyWorldUnitsIterator( Iterator );

	for ( std::vector<CGameObjectParam*>::const_iterator Begin = worldGameObjectParams.begin(),
		  End = worldGameObjectParams.end();
		  Begin != End;
		  Begin++ )
	{
		// All units created here should be CREATE_UNIT_OPTIMAZE_ITERATION
		CreateUnit( **Begin );
	}
}
